/*
* Copyright (C) 2020 ~ 2024 chaigec All Rights Reserved.
*
* Author:     chaigec <chaigec@163.com>
* Maintainer: chaigec <chaigec@163.com>

 * Use of this source code is governed by MIT license that can be found in the
 * LICENSE file in the root of the source tree. All contributing project authors
 * may be found in the AUTHORS file in the root of the source tree.
*/

#include "MkRtspClient.h"
#include "MkRtspSubSession.h"
#include "MkRtp/MkRtpUdpServer.h"
#include "MkRtspHeader.h"
#include "MkRtspUtil.h"
#include "MkRtp/MkRtpUtil.h"
#include "MkRtp/MkRtpUdpServer.h"
#include "MkSdp/MkSdp.h"
#include "MkUtil/MkBase64.h"
#include "MkUtil/MkLog.h"
#include "MkUtil/MkUtil.h"
#include "MkUtil/MkTime.h"

CMkRtspClient::CMkRtspClient(CMkEvent* pEvent, CMkTimer* pTimer, MkVector<CMkRtpUdpServer*>& VecRtpUdpServer, const MkEventRemovedCallback& RemovedCb)
    : CMkTcpClient(pEvent, pTimer, RemovedCb)
    , m_CSeq(1)
    , m_Scale(1.0)
    , m_Speed(1.0)
    , m_nRecvMaxLen(Len2K)
    , m_nRecvOffset(0)
    , m_VecRtpUdpServer(VecRtpUdpServer)
    , m_PlayMode("play")
{
    m_pRecvBuffer = (Uint8*)malloc(m_nRecvMaxLen);
    m_VecSupportMethods.push_back(MkRtspMethodOption);
}


Uint32 CMkRtspClient::SendDescribe()
{
    MkMap<MkRtspHeaderType, MkString> MapHeaders;
    MapHeaders[MkRtspHeaderAccept] = "application/sdp";
    return SendRtspRequest(m_Url, MkRtspMethodDescribe, MapHeaders);
}

Uint32 CMkRtspClient::SendAnnounce(const MkString& StringSdp)
{
    MkMap<MkRtspHeaderType, MkString> MapHeaders;
    MapHeaders[MkRtspHeaderContentType] = "application/sdp";
    return SendRtspRequest(m_Url, MkRtspMethodAnnounce, MapHeaders, StringSdp);
}

Uint32 CMkRtspClient::SendGetParameter()
{
    return NoneError;
}

Uint32 CMkRtspClient::SendOption()
{
    return SendRtspRequest(m_Url, MkRtspMethodOption);
}

Uint32 CMkRtspClient::SendSetup(CMkRtspSubSession* pSubSession, const MkString& TransportMode)
{
    MkString Uri = pSubSession->GetUri();
    if (Uri.empty()) {
        return InvalidParams;
    }
    if (!CMkUtil::BeginByString(Uri, "rtsp")) {
        Uri = GetUrl() + "/" + Uri;
        pSubSession->SetUri(Uri);
    }
    m_PlayMode = TransportMode;
    CMkRtspTransport Transport;
    Transport.SetUnicast(TRUE);
    Transport.SetMode(TransportMode);
    Transport.SetLowTransport(m_bStreamTcp ? "TCP" : "UDP");
    if (m_bStreamTcp) {
        Transport.SetRtpInterleaved(0);
    } else {
        Transport.SetClientRtpPort(pSubSession->GetRtpLocalUdpPort());
    }
    MkString StrTransport;
    Transport.ToClientTransportString(StrTransport);
    MkMap<MkRtspHeaderType, MkString> MapHeaders;
    MapHeaders[MkRtspHeaderTransport] = StrTransport;
    return SendRtspRequest(Uri, MkRtspMethodSetup, MapHeaders);
}

Uint32 CMkRtspClient::SendPause()
{
    return SendRtspRequest(m_Url, MkRtspMethodPause);
}

Uint32 CMkRtspClient::SendPlay()
{
    return SendRtspRequest(m_Url, MkRtspMethodPlay);
}

Uint32 CMkRtspClient::SendRecord()
{
    return SendRtspRequest(m_Url, MkRtspMethodRecord);
}

Uint32 CMkRtspClient::SendRedirect()
{
    return NoneError;
}

Uint32 CMkRtspClient::SendSetParameter()
{
    return NoneError;
}

Uint32 CMkRtspClient::SendTearDown()
{
    return SendRtspRequest(m_Url, MkRtspMethodTearDown);
}

Uint32 CMkRtspClient::OnResponseError(const MkRtspStatusCode& StatusCode)
{
    return NoneError;
}

Uint32 CMkRtspClient::OnDescribeResponse(const CMkRtspRequest&  RtspReq, const CMkRtspResponse& Res)
{
    return NoneError;
}

Uint32 CMkRtspClient::OnAnnounceResponse(const CMkRtspResponse& Res)
{
    return NoneError;
}

Uint32 CMkRtspClient::OnGetParameterResponse(const CMkRtspResponse& Res)
{
    return NoneError;
}

Uint32 CMkRtspClient::OnOptionResponse(const CMkRtspResponse& Res)
{
    m_VecSupportMethods.clear();
    m_VecSupportMethods.push_back(MkRtspMethodOption);
    const MkString Methods = Res.GetHeader(MkRtspHeaderPublic);
    if (Methods.empty()) {
        //set support all
        m_VecSupportMethods.push_back(MkRtspMethodDescribe);
        m_VecSupportMethods.push_back(MkRtspMethodAnnounce);
        m_VecSupportMethods.push_back(MkRtspMethodGetParameter);
        m_VecSupportMethods.push_back(MkRtspMethodOption);
        m_VecSupportMethods.push_back(MkRtspMethodPause);
        m_VecSupportMethods.push_back(MkRtspMethodPlay);
        m_VecSupportMethods.push_back(MkRtspMethodRecord);
        m_VecSupportMethods.push_back(MkRtspMethodRedirect);
        m_VecSupportMethods.push_back(MkRtspMethodSetup);
        m_VecSupportMethods.push_back(MkRtspMethodSetParameter);
        m_VecSupportMethods.push_back(MkRtspMethodTearDown);
    } else {
        MkVector<MkString> VesMethod;
        Uint32 ErrorCode = CMkUtil::StringSplit(Methods, ", ", VesMethod);
        if (1 == VesMethod.size()) {
            ErrorCode = CMkUtil::StringSplit(Methods, ",", VesMethod);
        }
        MkCheckErrorLog(ErrorCode, "split method:%s error rtsp:%s\n", Methods.c_str(), m_Url.c_str());
        MkVector<MkString>::iterator it = VesMethod.begin();
        MkRtspMethod RtspMethod;
        for (; it != VesMethod.end(); it++) {
            RtspMethod = CMkRtspUtil::GetInstance().GetMkMethod(*it);
            if (MkRtspMethodUnknown != RtspMethod) {
                m_VecSupportMethods.push_back(RtspMethod);
            }
        }
    }
    if (MkRtspStateUnauthorized == Res.GetStatusCode()) {
        SendOption();
        return RtspResponseErrorBegin + Res.GetStatusCode();
    }
    return NoneError;
}

Uint32 CMkRtspClient::OnPauseResponse(const CMkRtspResponse& Res)
{
    return NoneError;
}

Uint32 CMkRtspClient::OnPlayResponse(const CMkRtspResponse& Res)
{
    return NoneError;
}

Uint32 CMkRtspClient::OnRecordResponse(const CMkRtspResponse& Res)
{
    return NoneError;
}

Uint32 CMkRtspClient::OnRedirectResponse(const CMkRtspResponse& Res)
{
    return NoneError;
}

Uint32 CMkRtspClient::OnSetupResponse(const CMkRtspRequest&  RtspReq, const CMkRtspResponse& Res)
{
    const MkString Session = Res.GetHeader(MkRtspHeaderSession);
    MkVector<MkString> vecStr;
    Uint32 ErrorCode = CMkUtil::StringSplit(Session, ";", vecStr);
    Uint32 TimeOut = DefaultRtpTimeOut;
    if (vecStr.size() > 1) {
        for (size_t i = 1; i < vecStr.size(); i++) {
            size_t nPos = vecStr[i].find("=");
            if (MkString::npos != nPos && "timeout" == vecStr[i].substr(0, nPos)) {
                TimeOut = std::stoi(vecStr[i].substr(nPos + 1));
                break;
            }
        }
    }
    SetPlaySession(vecStr[0]);
    const MkString StrTransport = Res.GetHeader(MkRtspHeaderTransport);
    CMkRtspTransport Transport;
    Transport.ParseServerTransport(StrTransport);

    CMkRtspSubSession *pSubSession = nullptr;
    MkUnorderedMap<Uint32, CMkRtspSubSession*>::iterator it = m_MapSession.begin();
    for (; it != m_MapSession.end(); it++) {
        if (RtspReq.GetUri() == it->second->GetUri()) {
            pSubSession = it->second;
            if (pSubSession) {
                pSubSession->SetTimeOut(TimeOut);
                pSubSession->SetSsrc(Transport.GetSsrc());
                pSubSession->SetRtpRemotePort(Transport.GetServerRtpPort());
                if (pSubSession->GetRtpUdpServer()) {
                    MkCodecType Codec = pSubSession->GetCodec();
                    MkRtpRemoter Param;
                    Param.RtpRemoteHost = pSubSession->GetRtpRemoteHost();
                    Param.RtpRemotePort = Transport.GetServerRtpPort();
                    Param.Payload = pSubSession->GetPayloadType();
                    Param.RtcpPacketCb = [this](const CMkRtcpPacket& RtcpPacket, const MkString& Host, Uint16 Port) {
                        return OnRtcpPacket(RtcpPacket, Host, Port);
                    };
                    Param.RtpPacketCb = [this, Codec](const CMkRtpPacket& RtpPacket, const MkString& Host, Uint16 Port) {
                        return OnRtpPacket(Codec, RtpPacket, Host, Port);
                    };
                    Param.RtpType = MkRtpRecver;
                    Param.Ssrc = Transport.GetSsrc();
                    pSubSession->GetRtpUdpServer()->AddRtpRemoter(Transport.GetSsrc(), Param);
                }
            }
            break;
        }
    }
    it = m_MapSession.begin();
    for (; it != m_MapSession.end(); it++) {
        if (MaxUint32 == it->second->GetSsrc()) {
            //not set up
            ErrorCode = SendSetup(it->second, m_PlayMode);
            break;
        }
    }
    if (it != m_MapSession.end()) {
        ErrorCode = UnInited;
    }
    return ErrorCode;
}

Uint32 CMkRtspClient::OnSetParameterResponse(const CMkRtspResponse& Res)
{
    return NoneError;
}

Uint32 CMkRtspClient::OnTearDownResponse(const CMkRtspResponse& Res)
{
    return NoneError;
}

Uint32 CMkRtspClient::OnAuthenticator(OUT MkString& User, OUT MkString& Password, OUT BOOL& bPasswordMd5)
{
    return NoneError;
}

Uint32 CMkRtspClient::OnRtpPacket(const MkCodecType& Codec, const CMkRtpPacket& RtpPacket, const MkString& Host, Uint16 Port)
{
    return NoneError;
}

Uint32 CMkRtspClient::OnRtcpPacket(const CMkRtcpPacket& RtcpPacket, const MkString& Host, Uint16 Port)
{
    return NoneError;
}

Uint32 CMkRtspClient::OnRecv()
{
    Uint32 nToRead = m_nRecvMaxLen - m_nRecvOffset;
    if (0 == nToRead) {
        MkWarningLog("max len:%lu is too shot", m_nRecvMaxLen);
        Close();
        return SocketClosed;
    }
    Uint32 ErrorCode = Recv(m_pRecvBuffer + m_nRecvOffset, nToRead);
    MkCheckErrorLog(ErrorCode, "rtsp client recv failed\n");
    m_nRecvOffset += nToRead;
    Uint32 nUsed = 0;
    Uint32 nCurUsed = 0;
    while (nUsed < m_nRecvOffset) {
        nCurUsed = 0;
        if (0x24 == m_pRecvBuffer[nUsed]) {  //magic byte is 0x24
            ErrorCode = ParseRtspFrame(m_pRecvBuffer + nUsed, m_nRecvOffset - nUsed, nCurUsed);
        } else {
            ErrorCode = ParseRtspMessage(m_pRecvBuffer + nUsed, m_nRecvOffset - nUsed, nCurUsed);
        }
        if (UnInited == ErrorCode) {
        } else if (NotHasEnoughBuffer == ErrorCode) {
            break;
        } else if (SocketClosed == ErrorCode) {
            return ErrorCode;
        } else if (NoneError != ErrorCode) {
            MkWarningLog("rtsp recv error msg:%s\n", m_pRecvBuffer + nUsed);
            Close();
            return SocketClosed;
        }
        if (CMkRtspUtil::GetInstance().DebugMessageContent()) {
            MkString strContent = MkString((const Int8*)m_pRecvBuffer + nUsed, nCurUsed);
            MkDebugLog("rtsp client recv:\n%s\n", strContent.c_str());
        }
        nUsed += nCurUsed;
    }
    memmove(m_pRecvBuffer, m_pRecvBuffer + nUsed, m_nRecvOffset - nUsed);
    m_nRecvOffset -= nUsed;
    return NoneError;
}

Uint32 CMkRtspClient::GetRequestFromCSeq(Uint32 CSeq, CMkRtspRequest& RtspReq)
{
    Uint32 ErrorCode = InvalidParams;
    MkMap<Uint32, CMkRtspRequest>::iterator it = m_MapRequest.find(CSeq);
    if (it != m_MapRequest.end()) {
        RtspReq = it->second;
        ErrorCode = NoneError;
        m_MapRequest.erase(it);
    }
    return  ErrorCode;
}

BOOL CMkRtspClient::IsMethodSupported(const MkRtspMethod& Method)
{
    MkVector<MkRtspMethod>::iterator it = m_VecSupportMethods.begin();
    for (; it != m_VecSupportMethods.end(); it++) {
        if (Method == *it) {
            return TRUE;
        }
    }
    return FALSE;
}

Uint32 CMkRtspClient::ParseRtspMessage(Uint8* pBuf, Uint32 nLen, OUT Uint32& nUsed)
{
    CMkRtspResponse RtspRes;
    Uint32 ErrorCode = RtspRes.Parse((Int8*)pBuf, nLen, nUsed);
    MkCheckErrorLog(ErrorCode, "parse rtsp message failed\n");

    Uint32 CSeq = RtspRes.GetCSeq();
    CMkRtspRequest  RtspReq;
    ErrorCode = GetRequestFromCSeq(CSeq, RtspReq);
    MkCheckErrorLog(ErrorCode, "not send cseq:%d message message used:%d nlen:%d\n", CSeq, nUsed, nLen);

    if (MkRtspStateUnauthorized == RtspRes.GetStatusCode()) {
        const MkString Authorization = RtspReq.GetHeader(MkRtspHeaderAuthorization);
        if (!Authorization.empty()) {
            MkErrorLog("user or pass error rtspurl:%s\n", m_Url.c_str());
            //m_PlayState = MkRtspPlayStateAuthorFailed;
            return InvalidParams;
        }
        MkString UserName = m_Author.GetUserName();
        MkString Password = m_Author.GetPassword();
        if (UserName.empty()
            && Password.empty()) {
            BOOL bMd5Pass = FALSE;
            ErrorCode = OnAuthenticator(UserName, Password, bMd5Pass);
            MkCheckErrorLog(ErrorCode, "set user pass error rtspurl:%s\n", m_Url.c_str());
            m_Author.SetUser(UserName);
            m_Author.SetPassword(Password, bMd5Pass);
        }
        if (UserName.empty()
            && Password.empty()) {
            MkErrorLog("rtsp server:%s not set author user and pass\n", m_Url.c_str());
            return UnknownError;
        } else {
            BOOL bBasic = FALSE;
            MkString Realm;
            MkString Nonce;
            ErrorCode = RtspRes.ParseAuthenticate(bBasic, Realm, Nonce);
            MkCheckErrorLog(ErrorCode, "parase Authenticate failed rtspurl:%s\n", m_Url.c_str());
            //MkDebugLog("parse realm:%s  noce:%s basic:%d\n", Realm.c_str(), Nonce.c_str(), bBasic);
            m_Author.SetRealm(Realm);
            m_Author.SetNonce(Nonce);
            m_Author.SetBasic(bBasic);
        }
    } else if (MkRtspStateOk != RtspRes.GetStatusCode()) {
        OnResponseError(RtspRes.GetStatusCode());
        return UnknownError;
    }
    switch (RtspReq.GetMethod()) {
    case MkRtspMethodOption:
        ErrorCode = OnOptionResponse(RtspRes);
        break;
    case MkRtspMethodDescribe:
        ErrorCode = OnDescribeResponse(RtspReq, RtspRes);
        break;
    case MkRtspMethodAnnounce:
        ErrorCode = OnAnnounceResponse(RtspRes);
        break;
    case MkRtspMethodSetup:
        ErrorCode = OnSetupResponse(RtspReq, RtspRes);
        break;
    case MkRtspMethodPlay:
        ErrorCode = OnPlayResponse(RtspRes);
        break;
    case MkRtspMethodRecord:
        ErrorCode = OnRecordResponse(RtspRes);
        break;
    case MkRtspMethodTearDown:
        ErrorCode = OnTearDownResponse(RtspRes);
        break;
    default:
        break;
    }
    return ErrorCode;
}

Uint32 CMkRtspClient::ParseRtspFrame(Uint8* pBuf, Uint32 nLen, OUT Uint32& nUsed)
{
    if (nLen < 4) {
        return NotHasEnoughBuffer;
    }
    Uint8 Magic = pBuf[0];
    Uint8 Channel = pBuf[1];
    Uint32 FrameLen = (pBuf[2] << 8) | pBuf[3];
    if (FrameLen + 4 > nLen) {
        return NotHasEnoughBuffer;
    }
    //MkDebugLog("parse rtsp frame len:%d\n", FrameLen);
    nUsed = 4 + FrameLen;
    CMkRtpPacket RtpPacket;
    Uint32 ErrorCode = RtpPacket.Parse(pBuf + 4, FrameLen);
    MkCheckErrorLog(ErrorCode, "parse rtp packet failed\n");
    OnRtpPacket(MkCodecUnknown, RtpPacket, GetRemoteHost(), GetRemotePort());
    return NoneError;
}

Uint32 CMkRtspClient::ConnectRtspUrl(const MkString& RtspUrl)
{
    MkString Host;
    Uint16 Port;
    MkString Path;
    MkString UserName;
    MkString Password;
    Uint32 ErrorCode = CMkRtspUtil::GetInstance().RtspUrlParse(RtspUrl, Host, Port, UserName, Password, Path);
    MkCheckErrorLog(ErrorCode, "rtsp url parse error rtspurl:%s\n", m_Url.c_str());
    m_Author.SetUser(UserName);
    m_Author.SetPassword(Password, FALSE);
    m_Url = "rtsp://" + Host + ":" + std::to_string(Port) + Path;

    //m_PlayState = MkRtspPlayStateConnect;
    return ConnectServer(Host, Port, [this]() {
        SendOption();
    });
}

Uint32 CMkRtspClient::AddHeaderAuthorization(const MkString& Uri, const MkRtspMethod& Method, CMkRtspRequest&  RtspReq)
{
    MkString Response;
    if (m_Author.IsBasic()) {
        m_Author.GetBasicResponse(Response);
    } else {
        m_Author.GetDigestResponse(CMkRtspUtil::GetInstance().GetRtspMethod(Method), Uri, Response);
        Response = "Digest username=\"" + m_Author.GetUserName() + "\", realm=\"" + m_Author.GetRealm() + "\", nonce=\"" + m_Author.GetNonce() + "\", uri=\"" + Uri + "\", response=\"" + Response + "\", algorithm=\"MD5\"";
    }
    RtspReq.AddHeader(MkRtspHeaderAuthorization, Response);
    //m_Author.SetRealm("");  //after add header clear realm
    return NoneError;
}

Uint32 CMkRtspClient::SendRtspRequest(const MkString& Uri, const MkRtspMethod& Method, const MkMap<MkRtspHeaderType, MkString>& MapHeaders/* = MkMap<MkRtspHeaderType, MkString>()*/, const MkString& strBody/*=""*/)
{
    if (!IsMethodSupported(Method)) {
        MkErrorLog("rtsp server:%s unspported describe\n", Uri.c_str());
        return UnSupported;
    }
    CMkRtspRequest RtspReq;
    RtspReq.SetMethod(Method);
    RtspReq.SetUri(Uri);
    RtspReq.AddHeader(MkRtspHeaderUserAgent, MkUserAgent);
    RtspReq.AddHeader(MkRtspHeaderCSeq, std::to_string(m_CSeq));
    if (!m_Author.GetRealm().empty()) {
        AddHeaderAuthorization(Uri, Method, RtspReq);
    }
    if (!m_PlaySession.empty()) {
        RtspReq.AddHeader(MkRtspHeaderSession, m_PlaySession);
    }
    MkMap<MkRtspHeaderType, MkString>::const_iterator it = MapHeaders.begin();
    for (; it != MapHeaders.end(); it++) {
        RtspReq.AddHeader(it->first, it->second);
    }
    RtspReq.AddBody(strBody);

    MkString strReq;
    Uint32 ErrorCode = RtspReq.ToString(strReq);
    MkCheckErrorLog(ErrorCode, "request send  failed rtspurl:%s\n", m_Url.c_str());
    Uint32 nSendLen = strReq.size();
    m_MapRequest[m_CSeq] = RtspReq;
    m_CSeq++;
    if (CMkRtspUtil::GetInstance().DebugMessageContent()) {
        MkDebugLog("rtsp client send:\n%s\n", strReq.c_str());
    }
    return CMkSocket::Send((const Uint8*)strReq.c_str(), nSendLen);
}

CMkRtpUdpServer* CMkRtspClient::GetFreeRtpUdpServer() const
{
    Uint32 nMaxScore = 0;
    CMkRtpUdpServer* pUdpServer = nullptr;
    MkVector<CMkRtpUdpServer*>::const_iterator it = m_VecRtpUdpServer.begin();
    for (; it != m_VecRtpUdpServer.end(); it++) {
        if ((*it)->GetScore() > nMaxScore) {
            pUdpServer = *it;
            nMaxScore = (*it)->GetScore();
        }
    }
    return pUdpServer;
}

CMkRtspClient::~CMkRtspClient()
{
    MkUnorderedMap<Uint32, CMkRtspSubSession*>::iterator it = m_MapSession.begin();
    for (; it != m_MapSession.end(); it++) {
        MkDelete(it->second);
    }
    m_MapSession.clear();
    m_MapRequest.clear();
    MkFree(m_pRecvBuffer);
}